import re

from api import *


class Ejector(ImEjector):
    def __init__(self, config=None) -> None:
        super().__init__()
        print('>>> create Ejector')
        self.config = config
        self.minCodeLength = 1
        self.unique = ''
        self.rules = []
        self.applyConfig()

    def applyConfig(self):
        c = self.config
        self.minCodeLength = c.get('minCodeLength') or 1
        self.unique = c.get('unique') or 'current'
        if c.get('rules') is not None:
            for rule in c.get('rules'):
                self.rules.append(rule)

    def eject(self, old_state: CandidateState, new_state: CandidateState, im_service: ImService):
        """
        根据前后两个候选状态，计算是否需要提交
        :param old_state: 之前的候选状态
        :param new_state: 最新的候选状态
        :param im_service: 输入控制
        :return: 提交后保留的编码
        """
        print('>>> eject check.')
        if len(self.rules) == 0:  # 没由配置规则，手动上屏
            print('>>> eject None, no rules')
            return False
        codeLength = len(new_state.composition)
        if codeLength < self.minCodeLength:  # 码长不足
            print('>>> eject None, code too short')
            return False
        if not old_state.hasCandidate():  # 空候选无法上屏
            print('>>> eject None, no candidates')
            return False

        candidate = None
        eatLength = len(old_state.composition)
        for rule in self.rules:
            action = rule.get('action')
            if action == 'ejectUnique':
                candidate = self._ejectUnique(rule, new_state if self.unique == 'current' else old_state)
                if candidate is not None:
                    eatLength = len(candidate.code)
                    break
            elif action == 'ejectFirst':
                candidate = self._ejectFirst(rule, old_state, new_state.composition)
                if candidate is not None:
                    eatLength = min(eatLength, len(candidate.code))
                    break
        if candidate is None:
            print('>>> eject None.')
            return False
        else:
            # print('>>> eject `{}`, old_state:{}, new_state:{}'.format(candidate.text, old_state, new_state))
            im_service.commit(candidate.text)
            new_state.composition = new_state.composition[eatLength:]
            return True

    def _ejectUnique(self, rule, state: CandidateState) -> Candidate:
        candidate = None
        if state.count() == 1:
            first = state.candidateAt(0)
            if rule.get('code') == 'equals' and first.code == state.composition:
                candidate = first
        return candidate

    def _ejectFirst(self, rule, state: CandidateState, new_code: str) -> Candidate:
        candidate = None
        first = state.candidateAt(0)
        codeRule = rule.get('code')
        print('>>> ejectFirst with codeRule: {}, new_code: {}'.format(codeRule, new_code))
        if codeRule == 'equals':
            if first.code == state.composition:
                candidate = first
        elif codeRule.startswith('match:'):
            pattern = codeRule[6:]
            print('>>> matching:{} with:{}'.format(pattern, new_code))
            if re.match(pattern, new_code):
                candidate = first
        return candidate
